package com.example.springbootdemo.aop;

import com.example.springbootdemo.annotation.ProcessInfo;
import com.example.springbootdemo.entity.ProcessLog;
import com.example.springbootdemo.entity.SysUser;
import com.example.springbootdemo.mapper.ProcessLogMapper;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.Date;

/**
 * https://blog.csdn.net/ilovewqf/article/details/103198763
 * https://blog.csdn.net/baidu_32492845/article/details/79712141
 * @Component 用于定义一些中性化的组件
 * 处理用户的操作日志
 */
@Component
@Aspect
public class ProcessAdvisor {

    private ProcessLogMapper processLogMapper;

    public ProcessAdvisor(ProcessLogMapper processLogMapper) {
        this.processLogMapper = processLogMapper;
    }

    /**
     * 所有的加了 @ProcessInfo 这个注解的方法都会被通知到
     * @param pjp
     * @return
     */
    @Around("@annotation(com.example.springbootdemo.annotation.ProcessInfo)")
    public Object round(ProceedingJoinPoint pjp) {

        MethodSignature methodSignature = (MethodSignature)pjp.getSignature();
        // 获取调用的目标方法的方法名
        String methodName = methodSignature.getName();
        // 获取反射对应的方法
        Method method = methodSignature.getMethod();

        // method.getDeclaringClass().getName() 拿到方法对应的全类名
        String classInfo = method.getDeclaringClass().getName(); // 获取全类名

        // 获取方法头顶的 @ProcessInfo 注解
        ProcessInfo processInfoAnnotation = method.getDeclaredAnnotation(ProcessInfo.class);
        // 获取到 @ProcessInfo 中的内容
        String processInfo = processInfoAnnotation.value();

        // 获取方法的参数信息, 目的是获取参数名
        Parameter[] parameters = method.getParameters(); // 获取方法的参数

        StringBuffer paramString = new StringBuffer();
        // 因为有些方法是没有参数的
        if(null != parameters && parameters.length > 0) {
            Object[] args = pjp.getArgs();  // 获取方法的参数值

            for(int i = 0; i < parameters.length; i++) {
                Parameter p = parameters[i];  // 每个参数
                // p.getName() 拿到的是参数名:
                paramString.append(p.getName() + "=" + args[i] + ";");
            }
        }

        ProcessLog processLog = new ProcessLog();

        /**
         * 在spring中, 有些地方我们需要使用到 request, 或者 session, 但是我们的方法
         * 又无法提供的情况下, 那么就使用 RequestContextHolder.
         */
        HttpServletRequest request = ((ServletRequestAttributes)RequestContextHolder
                .currentRequestAttributes()).getRequest();
        HttpSession session = request.getSession();

        SysUser sysUser = (SysUser) session.getAttribute("userInfo");

        processLog.setUserId(sysUser.getId());

        processLog.setCreateTime(new Date());
        processLog.setClassName(classInfo);
        processLog.setMethodName(methodName);
        processLog.setProcessInfo(processInfo);
        processLog.setParamsInfo(paramString.toString());

        processLogMapper.insert(processLog);

        Object obj = null;

        try {
             obj = pjp.proceed();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        return obj;
    }
}
